<!-- 每个回话对应的聊天内容 -->
<script setup lang="ts">
import type { AnyObject } from 'typescript-api-pro';
import type { BubbleProps } from 'vue-element-plus-x/types/Bubble';
import type { BubbleListInstance } from 'vue-element-plus-x/types/BubbleList';
import type { FilesCardProps } from 'vue-element-plus-x/types/FilesCard';
import type { ThinkingStatus } from 'vue-element-plus-x/types/Thinking';
import { DocumentCopy, Download, StarFilled, WarningFilled } from '@element-plus/icons-vue';
import { ElMessage } from 'element-plus';
import { useHookFetch } from 'hook-fetch/vue';
import { Sender } from 'vue-element-plus-x';
import { useRoute } from 'vue-router';
import { send } from '@/api';
import { get_session } from '@/api/session';
import { getChatFiles, rateMessage } from '@/api/system';
import FilesSelect from '@/components/FilesSelect/index.vue';
import MermaidChart from '@/components/MermaidChart/index.vue';
import { useChatStore } from '@/stores/modules/chat';
import { useFilesStore } from '@/stores/modules/files';
import { useModelStore } from '@/stores/modules/model';
import { useSessionStore } from '@/stores/modules/session';
import { useUserStore } from '@/stores/modules/user';

type MessageItem = BubbleProps & {
  key: number;
  role: 'ai' | 'user' | 'system';
  avatar: string;
  thinkingStatus?: ThinkingStatus;
  thinlCollapse?: boolean;
  reasoning_content?: string;
  rate?: number;
  toolId?: number;
  images?: string[];
  docs?: { filename: string; filepath: string }[];
  completed?: boolean; // 添加completed字段
};

const route = useRoute();
const chatStore = useChatStore();
const modelStore = useModelStore();
const filesStore = useFilesStore();
const userStore = useUserStore();
const sessionStore = useSessionStore();

// 用户头像
const avatar = computed(() => {
  const userInfo = userStore.userInfo;
  return userInfo?.avatar || 'https://www.xqnmz.com/uc_server/avatar.php?uid=0&size=middle';
});

const currentSession = computed(() => sessionStore.currentSession);
const canSendMessage = computed(() => {
  // 如果 currentSession 为 null 或 undefined，则默认允许发送消息（向后兼容）
  if (!currentSession.value)
    return true;
  // 如果 is_sender 字段存在，则根据其值判断，否则默认允许发送消息
  return currentSession.value.is_sender !== false;
});

const inputValue = ref('');
const senderRef = ref<InstanceType<typeof Sender> | null>(null);
const bubbleItems = ref<MessageItem[]>([]);
const bubbleListRef = ref<BubbleListInstance | null>(null);
const sessionLoading = ref(false); // 会话详情加载状态

const { stream, loading: isLoading, cancel } = useHookFetch({
  request: send,
  onError: (_) => {
    // 错误拦截处理
  },
});
// 记录进入思考中
let isThinking = false;
// 记录SSE是否完成
const isSSEFinished = ref(false);

watch(
  () => route.params?.id,
  async (_id_) => {
    if (_id_) {
      // 优化：立即显示页面，异步加载数据
      bubbleItems.value = []; // 清空消息列表，准备加载

      if (_id_ !== 'not_login') {
        // 优化：并行加载会话详情和聊天记录，减少等待时间
        const loadPromises = [];

        // 1. 立即检查缓存中的聊天记录
        if (chatStore.chatMap[`${_id_}`] && chatStore.chatMap[`${_id_}`].length) {
          bubbleItems.value = chatStore.chatMap[`${_id_}`] as MessageItem[];
          // 立即滚动到底部
          nextTick(() => {
            bubbleListRef.value?.scrollToBubble(0);
          });
        }
        else {
          // 无缓存则异步请求聊天记录
          loadPromises.push(chatStore.requestChatList(`${_id_}`).then(() => {
            bubbleItems.value = chatStore.chatMap[`${_id_}`] as MessageItem[];
            nextTick(() => {
              bubbleListRef.value?.scrollToBubble(0);
            });
          }).catch(() => {
            // 静默处理错误，保持页面可用
            bubbleItems.value = [];
          }));
        }

        // 2. 异步加载会话详情（不阻塞页面渲染）
        sessionLoading.value = true;
        loadPromises.push(get_session(_id_ as string).then((sessionDetail) => {
          sessionStore.setCurrentSession(sessionDetail.data);

          // 设置页面title为sessionTitle
          if (sessionDetail.data && (sessionDetail.data as any).sessionTitle) {
            document.title = `${(sessionDetail.data as any).sessionTitle} - 新青年`;
          }
        }).catch(() => {
          // 如果获取会话详情失败，设置默认会话信息
          sessionStore.setCurrentSession({
            id: _id_ as string,
            content: '',
            is_sender: false, // 未登录用户默认没有发送权限
          });
        }).finally(() => {
          sessionLoading.value = false;
        }));

        // 等待所有异步操作完成
        await Promise.allSettled(loadPromises);
      }

      // 优化：延迟处理发送逻辑，确保页面先渲染完成
      const v = localStorage.getItem('chatContent');
      if (v) {
        // 获取保存的图片和文件ID
        const imageIdsStr = localStorage.getItem('imageIds');
        const docIdsStr = localStorage.getItem('docIds');
        const toolIdStr = localStorage.getItem('toolId');
        const imageIds = imageIdsStr ? JSON.parse(imageIdsStr) : [];
        const docIds = docIdsStr ? JSON.parse(docIdsStr) : [];
        const toolId = toolIdStr ? Number.parseInt(toolIdStr) : undefined;

        // 延迟发送，确保页面完全加载
        setTimeout(() => {
          startSSE(v, imageIds, docIds, toolId);
        }, 200); // 减少延迟时间

        // 清除localStorage中的临时数据
        localStorage.removeItem('chatContent');
        localStorage.removeItem('imageIds');
        localStorage.removeItem('docIds');
        localStorage.removeItem('toolId');
      }
    }
  },
  { immediate: true, deep: true },
);

// 跳转新窗口下载文件
function handleDownloadFile(fileName: string, fileUrl: string) {
  const a = document.createElement('a');
  a.href = fileUrl;
  a.download = fileName;
  a.target = '_blank';
  a.click();
}
// 封装数据处理逻辑
function handleDataChunk(chunk: AnyObject) {
  try {
    // 如果role为system且有content内容，则使用ElMessage显示
    if (chunk.choices?.[0].delta.role === 'system' && chunk.choices?.[0].delta.message) {
      ElMessage.success(chunk.choices?.[0].delta.message);
    }

    // 如果role为event，则将event_data中的id赋值给当前数据的key
    if (chunk.choices?.[0].delta.role === 'event' && chunk.choices?.[0].delta.event_data?.id) {
      // 将event_data中的id赋值给当前消息项的key字段
      if (bubbleItems.value.length > 0) {
        bubbleItems.value[bubbleItems.value.length - 1].key = chunk.choices?.[0].delta.event_data.id;
      }
    }

    // 检查finish_reason是否为stop
    const finishReason = chunk.choices?.[0].finish_reason;
    if (finishReason === 'stop' && bubbleItems.value.length > 0) {
      // 将当前消息项的completed设置为true
      bubbleItems.value[bubbleItems.value.length - 1].completed = true;
    }

    const reasoningChunk = chunk.choices?.[0].delta.reasoning_content;
    if (reasoningChunk) {
      // 开始思考链状态
      bubbleItems.value[bubbleItems.value.length - 1].thinkingStatus = 'thinking';
      bubbleItems.value[bubbleItems.value.length - 1].loading = true;
      bubbleItems.value[bubbleItems.value.length - 1].thinlCollapse = true;
      if (bubbleItems.value.length) {
        bubbleItems.value[bubbleItems.value.length - 1].reasoning_content += reasoningChunk;
      }
    }

    // 另一种思考中形式，content中有 <think></think> 的格式
    // 一开始匹配到 <think> 开始，匹配到 </think> 结束，并处理标签中的内容为思考内容
    const parsedChunk = chunk.choices?.[0].delta.content;
    if (parsedChunk) {
      const thinkStart = parsedChunk.includes('<think>');
      const thinkEnd = parsedChunk.includes('</think>');
      if (thinkStart) {
        isThinking = true;
      }
      if (thinkEnd) {
        isThinking = false;
      }
      if (isThinking) {
        // 开始思考链状态
        bubbleItems.value[bubbleItems.value.length - 1].thinkingStatus = 'thinking';
        bubbleItems.value[bubbleItems.value.length - 1].loading = true;
        bubbleItems.value[bubbleItems.value.length - 1].thinlCollapse = true;
        if (bubbleItems.value.length) {
          bubbleItems.value[bubbleItems.value.length - 1].reasoning_content += parsedChunk
            .replace('<think>', '')
            .replace('</think>', '');
        }
      }
      else {
        // 结束 思考链状态
        bubbleItems.value[bubbleItems.value.length - 1].thinkingStatus = 'end';
        bubbleItems.value[bubbleItems.value.length - 1].loading = false;
        if (bubbleItems.value.length) {
          bubbleItems.value[bubbleItems.value.length - 1].content += parsedChunk;
        }
      }
    }
  }
  catch (err) {
    // 这里如果使用了中断，会有报错，可以忽略不管

  }
}

// 封装错误处理逻辑
function handleError(_: any) {
  // Fetch错误处理
}

async function startSSE(chatContent: string, imageIds: number[] = [], docIds: number[] = [], toolId?: number) {
  try {
    // 重置SSE完成状态
    isSSEFinished.value = false;

    // 如果没有传入imageIds和docIds参数，则从filesStore中获取
    let finalImageIds = imageIds;
    let finalDocIds = docIds;

    // 只有当传入的参数为空时，才从filesStore中获取
    if (imageIds.length === 0 && docIds.length === 0) {
      finalImageIds = filesStore.filesList
        .filter(file => file.file.type.startsWith('image/') && file.fileId)
        .map(file => file.fileId) as number[];

      finalDocIds = filesStore.filesList
        .filter(file => !file.file.type.startsWith('image/') && file.fileId)
        .map(file => file.fileId) as number[];

      // 点击发送时立即清除文件列表
      filesStore.setFilesList([]);
    }

    // 清空输入框
    inputValue.value = '';

    // 优化：并行处理消息添加和文件获取
    const addMessagePromise = new Promise<void>((resolve) => {
      // 立即添加用户消息，不等待文件获取
      addMessage(chatContent, true, [], []);
      addMessage('', false);
      resolve();
    });

    // 异步获取文件信息（不阻塞消息显示）
    let imageUrls: string[] = [];
    let docUrls: { filename: string; filepath: string }[] = [];

    if (finalImageIds.length > 0 || finalDocIds.length > 0) {
      getChatFiles({ image_ids: finalImageIds, doc_ids: finalDocIds }).then((fileResponse) => {
        if (fileResponse.code === 200) {
          const { images = [], docs = [] } = fileResponse.data || {};
          imageUrls = images;
          docUrls = docs;

          // 更新最后一条用户消息的文件信息
          if (bubbleItems.value.length >= 2) {
            const userMessageIndex = bubbleItems.value.length - 2;
            if (bubbleItems.value[userMessageIndex].role === 'user') {
              bubbleItems.value[userMessageIndex].images = imageUrls;
              bubbleItems.value[userMessageIndex].docs = docUrls;
            }
          }
        }
      }).catch(() => {
        // 静默处理错误，不影响消息发送
      });
    }

    // 等待消息添加完成
    await addMessagePromise;

    // 这里有必要调用一下 BubbleList 组件的滚动到底部 手动触发 自动滚动
    bubbleListRef.value?.scrollToBottom();

    // 获取当前会话的toolId（如果存在）
    const currentSessionToolId = (currentSession.value as any)?.toolId;

    for await (const chunk of stream({
      messages: bubbleItems.value
        .filter((item: any) => item.role === 'user')
        .map((item: any) => ({
          role: item.role,
          content: item.content,
        })),
      sessionId: route.params?.id !== 'not_login' ? String(route.params?.id) : undefined,
      userId: userStore.userInfo?.userId,
      model: modelStore.currentModelInfo?.modelName ?? '',
      modelId: modelStore.currentModelInfo?.id, // 传递当前模型ID
      // 如果有传入的toolId则使用它，否则使用当前会话的toolId
      toolId: toolId || currentSessionToolId,
      image_ids: finalImageIds, // 传递图片ID数组
      doc_ids: finalDocIds, // 传递文件ID数组
    })) {
      handleDataChunk(chunk.result as AnyObject);
    }
  }
  catch (err) {
    handleError(err);
  }
  finally {
    // 标记SSE已完成
    isSSEFinished.value = true;
    // 停止打字器状态
    if (bubbleItems.value && bubbleItems.value.length) {
      bubbleItems.value[bubbleItems.value.length - 1].typing = false;
    }
  }
}

// 中断请求
async function cancelSSE() {
  cancel();
  // 标记SSE已完成
  isSSEFinished.value = true;
  // 结束最后一条消息打字状态
  if (bubbleItems.value && bubbleItems.value.length) {
    bubbleItems.value[bubbleItems.value.length - 1].typing = false;
    // 添加[中断]标识到当前消息内容末尾
    if (bubbleItems.value[bubbleItems.value.length - 1].content) {
      bubbleItems.value[bubbleItems.value.length - 1].content += '[中断]';
    }
    else {
      bubbleItems.value[bubbleItems.value.length - 1].content = '[中断]';
    }
    bubbleItems.value[bubbleItems.value.length - 1].loading = false;
  }

  // 确保输入框的loading状态也被重置
  if (senderRef.value) {
    // senderRef.value.loading = false;
  }

  // 更新isLoading状态
  isLoading.value = false;
}

// 添加消息 - 维护聊天记录
function addMessage(message: string, isUser: boolean, images?: string[], docs?: { filename: string; filepath: string }[]) {
  const i = bubbleItems.value?.length || 0;
  const obj: MessageItem = {
    key: i,
    avatar: isUser
      ? avatar.value
      : 'https://www.xqnmz.com/static/image/default.jpg',
    avatarSize: '32px',
    role: isUser ? 'user' : 'system',
    placement: isUser ? 'end' : 'start',
    isMarkdown: false,
    loading: !isUser,
    content: message || '',
    images: images || [],
    docs: docs || [],
    reasoning_content: '',
    thinkingStatus: 'start',
    thinlCollapse: false,
    rate: 0,
    completed: false, // 初始化completed为false
  };
  if (bubbleItems.value) {
    bubbleItems.value.push(obj);
  }
  else {
    bubbleItems.value = [obj];
  }
}

// 展开收起 事件展示
function handleChange(_: { value: boolean; status: ThinkingStatus }) {
  // 处理展开收起状态变化
}

// 复制消息内容到剪贴板
function copyMessageContent(content: string) {
  if (!content) {
    ElMessage.warning('没有内容可以复制');
    return;
  }

  // 使用 Clipboard API 复制文本
  navigator.clipboard.writeText(content)
    .then(() => {
      ElMessage.success('内容已复制到剪贴板');
    })
    .catch((_) => {
      ElMessage.error('复制失败');
    });
}

// 处理评分变化
function handleRateChange(rate: number, item: MessageItem) {
  rateChatMessage(item.key, rate);
}

// 调用系统评分接口
async function rateChatMessage(messageId: number, rate: number) {
  try {
    const response = await rateMessage({ id: String(messageId), rate });
    if (response.code === 200) {
      ElMessage.success('评分成功');
    }
    else {
      ElMessage.error('评分失败');
    }
  }
  catch (error) {
    console.error('评分请求失败:', error);
    ElMessage.error('评分失败');
  }
}

function handleDeleteCard(_item: FilesCardProps, index: number) {
  filesStore.deleteFileByIndex(index);
}

watch(
  () => filesStore.filesList.length,
  (val) => {
    if (val > 0) {
      nextTick(() => {
        senderRef.value?.openHeader();
      });
    }
    else {
      nextTick(() => {
        senderRef.value?.closeHeader();
      });
    }
  },
);
</script>

<template>
  <div class="chat-with-id-container">
    <!-- 会话详情加载状态 -->
    <div v-if="sessionLoading" class="session-loading-overlay">
      <div class="loading-container">
        <el-icon class="loading-icon">
          <Loading />
        </el-icon>
      </div>
    </div>

    <div class="chat-warp">
      <BubbleList ref="bubbleListRef" :list="bubbleItems" :show-back-button="false" max-height="calc(100vh - 60px)">
        <template #header="{ item }">
          <Thinking
            v-if="item.reasoning_content" v-model="item.thinlCollapse" :content="item.reasoning_content"
            :status="item.thinkingStatus" class="thinking-chain-warp" @change="handleChange"
          />
        </template>

        <template #content="{ item }">
          <!-- 遍历item.files 展示图片 -->
          <el-image
            v-for="(file, index) in item.images" :key="index"
            style="width: 100px; height: 100px"
            :src="file"
            :preview-src-list="[file]"
            fit="cover"
          />
          <hr v-if="item.images && item.images.length > 0">
          <el-text
            v-for="(doc, index) in item.docs" :key="index"
            class="docfile"
            type="primary" @click="handleDownloadFile(doc.filename, doc.filepath)"
          >
            <el-icon><Download /></el-icon> {{ doc.filename }}
          </el-text>
          <hr v-if="item.docs && item.docs.length > 0">
          <!-- chat 内容走 markdown -->
          <XMarkdown
            v-if="item.content && item.role !== 'user'"
            :markdown="item.content"
            class="markdown-body"
            :themes="{ light: 'github-light', dark: 'github-dark' }"
            default-theme-mode="dark"
            :mermaid-config="{
              showDownload: false,
            }"
          >
            <template #img="{ ...props }">
              <el-image
                :key="props.key"
                :src="props.src"
                :preview-src-list="[props.src]"
                fit="cover"
              />
            </template>
            <template #code="{ ...props }">
              <!-- 自定义代码块渲染 -->
              <pre v-if="props.language != 'mermaid'" class="custom-code-block">
                <code :class="`language-${props.language}`">{{ props.content }}</code>
              </pre>
              <MermaidChart v-else :key="props.key" :content="props.content" />
            </template>
          </XMarkdown>
          <!-- user 内容 纯文本 -->
          <div v-if="item.content && item.role === 'user'" class="user-content">
            {{ item.content }}
          </div>
        </template>

        <!-- 自定义 loading -->
        <template #loading="{ }">
          <div class="loading-container">
            <el-icon class="loading-icon">
              <Loading />
            </el-icon>
            <span class="loading-text">{{ currentSession?.loading_text || '' }}</span>
          </div>
        </template>

        <template #footer="{ item }">
          <div v-if="item.role !== 'user' && item.completed" class="footer-wrapper">
            <div class="footer-container">
              <el-button
                color="#626aef"
                :icon="DocumentCopy"
                size="small"
                style="margin-top: -8px; margin-right: 12px;"
                circle
                @click="copyMessageContent(item.content)"
              />
              <el-rate
                v-model="item.rate"
                size="large"
                :void-icon="StarFilled"
                :icons="[StarFilled, StarFilled, StarFilled]"
                @change="handleRateChange($event, item)"
              />
            </div>
            <el-button type="text" :icon="WarningFilled" style="margin-left: 16px;">
              AI生成，仅供参考
            </el-button>
          </div>
        </template>
      </BubbleList>

      <Sender
        v-if="canSendMessage"
        ref="senderRef" v-model="inputValue" class="chat-defaul-sender" :auto-size="{
          maxRows: 2,
          minRows: 1,
        }" variant="updown" clearable :allow-speech="modelStore?.currentModelInfo?.voice_enabled === true" :loading="isLoading" @submit="startSSE" @cancel="cancelSSE"
      >
        <template #header>
          <div class="sender-header p-12px pt-6px pb-0px">
            <Attachments :items="filesStore.filesList" :hide-upload="true" @delete-card="handleDeleteCard">
              <template #prev-button="{ show, onScrollLeft }">
                <div
                  v-if="show"
                  class="prev-next-btn left-8px flex-center w-22px h-22px rounded-8px border-1px border-solid border-[rgba(0,0,0,0.08)] c-[rgba(0,0,0,.4)] hover:bg-#f3f4f6 bg-#fff font-size-10px"
                  @click="onScrollLeft"
                >
                  <el-icon>
                    <ArrowLeftBold />
                  </el-icon>
                </div>
              </template>

              <template #next-button="{ show, onScrollRight }">
                <div
                  v-if="show"
                  class="prev-next-btn right-8px flex-center w-22px h-22px rounded-8px border-1px border-solid border-[rgba(0,0,0,0.08)] c-[rgba(0,0,0,.4)] hover:bg-#f3f4f6 bg-#fff font-size-10px"
                  @click="onScrollRight"
                >
                  <el-icon>
                    <ArrowRightBold />
                  </el-icon>
                </div>
              </template>
            </Attachments>
          </div>
        </template>
        <template #prefix>
          <div class="flex-1 flex items-center gap-8px flex-none w-fit overflow-hidden">
            <FilesSelect />
          </div>
        </template>
      </Sender>
    </div>
  </div>
</template>

<style scoped lang="scss">
.chat-with-id-container {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: 100%;
  height: 100%;
  .chat-warp {
    display: flex;
    flex-direction: column;
    width: 100%;
    height: calc(100vh - 60px);

    // 当未登录隐藏Sender时，聊天内容区域自适应高度
    &:has(.el-bubble-list:only-child) {
      .el-bubble-list {
        height: 100%;
        max-height: 100% !important;
      }
    }
    .thinking-chain-warp {
      margin-bottom: 12px;
    }
  }
  :deep() {
    h1,h2{
      font-size: 18px!important;
    }
    h3,h4,h5,h6,h7{
      font-size: 17px!important;
    }
    .el-sender-content {
      textarea{
        font-size: 16px!important;
      }
    }
    .el-send-button:first-child {
      .el-button {
        color: #ffffff!important;
        background-color: #409eff!important;
        border: 1px solid #409eff!important;
      }
      .el-button.is-disabled {
        color: #ffffff!important;
        background-color: #88c3ff!important;
        border: 1px solid #88c3ff!important;
      }
      .loading-svg {
        color: #ffffff!important;
      }
    }

    /* 调整Sender组件下所有深层的el-icon大小 */
    .el-sender .el-icon {
      font-size: 20px!important; /* 调大图标 */
      svg {
        width: 20px!important;
        height: 20px!important;
      }
    }
    .el-sender-action-list-presets{
      padding: 0!important;
    }
    .el-bubble-list {
      padding-top: 24px;
      .el-bubble-content{
        max-width: 90%;
        padding: 12px;
        background-color: #f0f2f5;
        border-radius: 8px;
        .user-content  {
          font-size: 16px;
        }
      }
    }
    .loading-text{
      font-size: 16px;
    }
    .el-bubble {
      padding: 0 12px;
      padding-bottom: 24px;
    }
    .el-image{
      margin-right: 8px;
    }
    .el-typewriter {
      overflow: hidden;
      border-radius: 12px;
    }
    .user-content {
      // 换行
      white-space: pre-wrap;
    }
    .markdown-body {
      background-color: transparent;
    }
    .markdown-elxLanguage-header-div {
      top: -25px !important;
    }
    .docfile{
      cursor: pointer;
    }

    // xmarkdown 样式
    .elx-xmarkdown-container {
      padding: 0;
    }

    // 评分组件样式
    .el-rate__icon {
      margin-right: 0;
    }
  }
  .chat-defaul-sender {
    width: calc(100% - 110px);
    margin: 0 auto;
  }
  .footer-wrapper {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
}

  /* Loading 动画 */
  .loading-container {
    display: flex;
    gap: 8px;
    align-items: center;
  }
  .loading-icon {
    color: #0057ff;
    animation: spin 1.5s linear infinite;
  }

  /* 会话详情加载遮罩 */
  .session-loading-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: rgb(255 255 255 / 80%);
    backdrop-filter: blur(4px);
    .loading-container {
      display: flex;
      flex-direction: column;
      gap: 16px;
      align-items: center;
      padding: 24px;
      background: white;
      border-radius: 12px;
      box-shadow: 0 10px 25px rgb(0 0 0 / 10%);
      .loading-icon {
        font-size: 32px;
      }
      .loading-text {
        font-size: 16px;
        font-weight: 500;
        color: #333333;
      }
    }
  }

  @keyframes spin {
    0% {
      transform: rotate(0deg);
    }
    100% {
      transform: rotate(360deg);
    }
  }
}

// 手机版响应式优化
@media (width <= 768px) {
  .chat-with-id-container {
    height: 100%!important;
    overflow: hidden;
    .chat-warp {
      .el-bubble-list {
        height: calc(100% - 100px);
      }
    }
    .chat-defaul-sender {
      width: 100%;
    }
    :deep() {
      .el-bubble-list {

        .el-bubble-content {
          padding: 10px;
        }
        .el-bubble:nth-child(odd) {
          .el-bubble-content {
            max-width: calc(100% - 30px)!important;
          }
        }

        .el-bubble:nth-child(even) {
          .el-bubble-content {
            max-width: 100% !important;
          }
        }

        // 调整气泡间距
        .el-bubble {
          padding: 0 4px;

          --el-bubble-avatar-placeholder-gap:4px!important;
          --el-bubble-avatar-placeholder-width: 24px!important;
          --el-bubble-avatar-placeholder-height: 24px!important;
          .elx-xmarkdown-provider>div * {
            padding: 0!important;
          }
          .elx-xmarkdown-provider>div ol {
            padding-left: 16px!important;
          }
        }

      }
    }
  }
}
</style>
